﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace конфигуратор
{
    public class ДинамикОбъект   :INotifyPropertyChanged
    {
        private Паспорт _паспорт;
        Паспорт Паспорт
        {
            get { return _паспорт; }
            set
            {  
                //if (_паспорт!=null)
                //{
                //    _паспорт.ДинамикОбъект = null;
                //    _паспорт.PropertyChanged -= ПаспортPropertyChanged;
                //    _паспорт = null;
                //}
                if ( value != null )
                {
                    _паспорт = value;
                    _паспорт.ДинамикОбъект = this;
                    _паспорт.PropertyChanged += ПаспортPropertyChanged;
                }
            }
        }
        public object Объект{
                                        get { return _паспорт.Объект; } 
                                        set {  _паспорт.Объект = value;}
                                        }  
        public Type Тип { 
                                    get { return  _паспорт.Тип ; }
                                    set {  _паспорт.Тип = value; }
                                }
        public ДинамикОбъект (  )  {Паспорт= new Паспорт();}
        public ДинамикОбъект ( Паспорт паспорт)
        {
            Паспорт = паспорт;
            ЗаполнитьЧлены(); 
        }

        public ДинамикОбъект ( object объект )
        {
            Паспорт = new Паспорт();
            Объект = объект;
        }
        public ДинамикОбъект ( Type тип )
        {
            Паспорт = new Паспорт();
            Тип = тип;
        }

         void ПаспортPropertyChanged ( object sender, PropertyChangedEventArgs e )
        {
             if (e.PropertyName=="Тип"|| e.PropertyName=="Объект")
            ЗаполнитьЧлены();
        }
        void ЗаполнитьЧлены()
        {
                ОписаниеТипа= new ОписаниеТипа(Тип);
                Поля = Поле.ДатьСписокПолей( this);
                Свойства = СвойствоОболочка.ДатьСписокСвойств(this);
                Методы = Метод.ДатьСписокМетодов(this);
                Конструкторы = Конструктор.ДатьСписокКонструкторов(this);
                События = Событие.ДатьСписокСобытий(this);
                Элементы = ЭлементМассива.ДатьЭлементыМассива( this );
                //Элементы = ДинСписок.ДатьСписок( this );

                Обновить();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void Обновить ()
        {
            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs("") );
            if ( Поля != null )
            {
                foreach (var поле in Поля)
                {
                    поле.Обновить();
                }
            }
            if ( Свойства != null )
            {
                foreach (var свойство in Свойства)
                {
                    свойство.Обновить();
                }
            }
        }

        public ОписаниеТипа ОписаниеТипа { get; set; }
        public object АтрибутыПользовательские { get { return Тип.GetCustomAttributes(false); } }

        public bool ЭтоСписок { get { return Объект is IList; } }

        public Type ТипЭлемента 
        {
            get
            {
                if ( Тип.IsGenericType )
                {
                    return Тип.GetGenericArguments()[0];
                }
                if ( Тип.IsArray )
                {
                    return Тип.GetElementType();
                }
                return null;
            }
        }
        public object Элементы { get; set; }
        public List<Конструктор> Конструкторы { get; set; }
        public object События { get; set; }
        public List<Поле> Поля { get; set; }
        public List<Поле> ПоляОбъекта { get    {      return Поля!=null? Поля.Where(поле => !поле.F.IsStatic).ToList():null;   } }
        public List<Поле> ПоляТипа { get { return Поля!=null?  Поля.Where( поле => поле.F.IsStatic ).ToList():null; } }
        public List<СвойствоОболочка> Свойства { get; set; }
        public List<СвойствоОболочка> СвойстваОбъекта { get { return Свойства!=null? Свойства.Where( свойство => !свойство.МетодИнфоСвойства.IsStatic ).ToList():null; } }
        public List<СвойствоОболочка> СвойстваТипа { get { return Свойства!=null? Свойства.Where( свойство => свойство.МетодИнфоСвойства.IsStatic ).ToList():null; } }
        public List<Метод> Методы { get; set; }
        public List<Метод> МетодыОбъекта { get { return Методы!=null? Методы.Where(метод => !метод.МетодИнфо.IsStatic).ToList():null; } }
        public List<Метод> МетодыТипа { get { return Методы != null ? Методы.Where( метод => метод.МетодИнфо.IsStatic ).ToList() : null; } }

        //public List<Type> Предки { get; set; } 

      


        public static bool ЯвляетсяФлаговымПеречислителем (Type тип)
        {
              var atrib = тип.GetCustomAttributes(false).ToList();
              var flagAtt=    atrib.Find(obj => obj.GetType() == typeof (FlagsAttribute));
              return flagAtt != null;
        }



        public object ВызватьКонструктор ( params  object[] значенияПараметров )
        {
            if ( Тип == null ) return null;
            Type[] типыПараметров;
            if ( значенияПараметров == null )
            {
                типыПараметров = Type.EmptyTypes;
            }
            else
            {
                //var типы = значенияПараметров.Select( з => з.GetType()).ToArray();
                типыПараметров = new Type[значенияПараметров.Length];
                var i = 0;
                foreach ( var o in значенияПараметров )
                {
                    типыПараметров[i] = o.GetType();
                    i++;
                }
            }

            ConstructorInfo ctor = this.Тип.GetConstructor( типыПараметров );
            if ( ctor == null )
                return null;
            return ctor.Invoke( значенияПараметров );
        }
        public object ПолучитьСвойство ( string имяСвойства )
        {
            if ( Тип == null ) return null;
            return Тип.InvokeMember( имяСвойства, BindingFlags.GetProperty, null /* Binder */,Объект, null /* args */);
        }
        public static object ПолучитьСвойствоОбъекта (object объект,  string имяСвойства )
        {
            if ( объект == null ) return null;
            try
            {
                  return объект.GetType().InvokeMember( имяСвойства, BindingFlags.GetProperty, null /* Binder */, объект, null /* args */);
            }
            catch (Exception)
            {

                return null;
            }
           
        }

        public object УстановитьСвойство ( string имяСвойства, object значение )
        {
            if ( Тип == null ) return null;
            try
            {
                return Тип.InvokeMember( имяСвойства, BindingFlags.SetProperty, null /* Binder */, Объект, new object[] { значение } );
            }
            catch
            {
                return null;
            }

        }
        public object ПолучитьПоле ( string имяПоля )
        {
            if ( Тип == null ) return null;
            return Тип.InvokeMember( имяПоля, BindingFlags.GetField, null /* Binder */, Объект, null /* args */);
        }
        public static object ПолучитьПолеОбъекта (object объект, string имяПоля )
        {
            if ( объект == null ) return null;
            try
            {
                  return объект.GetType().InvokeMember( имяПоля, BindingFlags.GetField, null /* Binder */, объект, null /* args */);
            }
            catch (Exception)
            {

                return null;
            }
            
        }

        public object УстановитьПоле ( string имяПоля, object значениеПоля )
        {
            if ( Тип == null ) return null;
            try
            {
                return Тип.InvokeMember( имяПоля, BindingFlags.SetField, null /* Binder */, Объект ,new object[] { значениеПоля } );
            }
            catch
            {
                return null;
            }
            
        }
      

        public object ВызватьМетод ( string имяМетода, Type[] типыПараметров, object[] значениеПараметров )
        {
            if ( типыПараметров.Length != значениеПараметров.Length )
                throw new Exception( "разная длина параметров" );
            var метод = Тип.GetMethod( имяМетода, типыПараметров );
            if ( метод == null ) throw new Exception( "нет такого метода" );
            return метод.Invoke( Объект, BindingFlags.InvokeMethod, null, значениеПараметров, null );
        }
        public object ВызватьМетод ( string имяМетода, params object[] значениеПараметров )
        {
            try
            {
                return Тип.InvokeMember( имяМетода, BindingFlags.InvokeMethod, null, Объект, значениеПараметров );
            }
            catch ( Exception )
            {
                return null;
            }

        }
    }
}
